#!/usr/bin/env python3
"""
全局安全监控系统
实时监控和响应安全威胁
"""

import time
import json
import threading
from datetime import datetime, timedelta
from collections import defaultdict, deque
from typing import Dict, List, Any, Optional
import redis
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import requests
import logging

class SecurityMonitor:
    """安全监控系统"""
    
    def __init__(self, redis_client, config=None):
        self.redis_client = redis_client
        self.config = config or {}
        self.running = False
        self.monitor_thread = None
        
        # 监控配置
        self.monitor_config = {
            'check_interval': 60,  # 检查间隔（秒）
            'alert_threshold': {
                'failed_logins': 10,      # 失败登录次数
                'rate_limit_hits': 50,    # 频率限制触发次数
                'suspicious_ips': 5,      # 可疑IP数量
                'attack_attempts': 20     # 攻击尝试次数
            },
            'time_window': 300,  # 时间窗口（秒）
        }
        
        # 告警配置
        self.alert_config = {
            'email': {
                'enabled': False,
                'smtp_server': 'smtp.gmail.com',
                'smtp_port': 587,
                'username': '',
                'password': '',
                'recipients': []
            },
            'webhook': {
                'enabled': False,
                'url': '',
                'headers': {}
            },
            'sms': {
                'enabled': False,
                'api_url': '',
                'api_key': '',
                'recipients': []
            }
        }
        
        # 实时统计
        self.stats = {
            'total_requests': 0,
            'blocked_requests': 0,
            'threats_detected': 0,
            'ips_blacklisted': 0
        }
        
        # 威胁检测规则
        self.threat_rules = {
            'brute_force': {
                'pattern': 'failed_login',
                'threshold': 5,
                'window': 300,
                'action': 'blacklist_ip'
            },
            'ddos': {
                'pattern': 'rate_limit_exceeded',
                'threshold': 100,
                'window': 60,
                'action': 'blacklist_ip'
            },
            'injection_attack': {
                'pattern': 'invalid_input',
                'threshold': 3,
                'window': 300,
                'action': 'blacklist_ip'
            },
            'suspicious_activity': {
                'pattern': 'multiple_endpoints',
                'threshold': 20,
                'window': 60,
                'action': 'flag_ip'
            }
        }

    def start_monitoring(self):
        """启动安全监控"""
        if self.running:
            return
        
        self.running = True
        self.monitor_thread = threading.Thread(target=self._monitor_loop, daemon=True)
        self.monitor_thread.start()
        print("🔍 安全监控系统已启动")

    def stop_monitoring(self):
        """停止安全监控"""
        self.running = False
        if self.monitor_thread:
            self.monitor_thread.join()
        print("🔍 安全监控系统已停止")

    def _monitor_loop(self):
        """监控主循环"""
        while self.running:
            try:
                self._check_security_events()
                self._analyze_threats()
                self._update_statistics()
                time.sleep(self.monitor_config['check_interval'])
            except Exception as e:
                print(f"监控循环错误: {str(e)}")
                time.sleep(10)

    def _check_security_events(self):
        """检查安全事件"""
        try:
            # 获取最近的安全日志
            cutoff_time = datetime.now() - timedelta(seconds=self.monitor_config['time_window'])
            
            events = []
            for key in self.redis_client.scan_iter(match="security_log:*"):
                log_data = self.redis_client.get(key)
                if log_data:
                    try:
                        event = json.loads(log_data)
                        event_time = datetime.fromisoformat(event['timestamp'].replace('Z', '+00:00'))
                        if event_time >= cutoff_time:
                            events.append(event)
                    except Exception:
                        continue
            
            # 分析事件
            self._analyze_events(events)
            
        except Exception as e:
            print(f"检查安全事件失败: {str(e)}")

    def _analyze_events(self, events: List[Dict]):
        """分析安全事件"""
        # 按IP分组统计
        ip_stats = defaultdict(lambda: defaultdict(int))
        
        for event in events:
            ip = event.get('client_ip', 'unknown')
            event_type = event.get('event_type', 'unknown')
            ip_stats[ip][event_type] += 1
        
        # 检查威胁规则
        for ip, stats in ip_stats.items():
            for rule_name, rule in self.threat_rules.items():
                pattern = rule['pattern']
                threshold = rule['threshold']
                
                if pattern in stats and stats[pattern] >= threshold:
                    self._handle_threat(ip, rule_name, stats[pattern], rule)

    def _handle_threat(self, ip: str, threat_type: str, count: int, rule: Dict):
        """处理威胁"""
        action = rule.get('action', 'log')
        
        threat_data = {
            'ip': ip,
            'threat_type': threat_type,
            'count': count,
            'timestamp': datetime.utcnow().isoformat(),
            'action': action
        }
        
        # 记录威胁
        threat_key = f"threat:{int(time.time())}:{ip}"
        self.redis_client.setex(threat_key, 86400 * 7, json.dumps(threat_data))
        
        # 执行相应动作
        if action == 'blacklist_ip':
            self._blacklist_ip(ip, threat_type, 3600)  # 封禁1小时
        elif action == 'flag_ip':
            self._flag_suspicious_ip(ip, threat_type)
        
        # 发送告警
        self._send_alert(threat_data)
        
        print(f"🚨 威胁检测: {threat_type} - IP: {ip} - 次数: {count} - 动作: {action}")

    def _blacklist_ip(self, ip: str, reason: str, duration: int):
        """将IP加入黑名单"""
        blacklist_data = {
            'ip': ip,
            'reason': reason,
            'timestamp': datetime.utcnow().isoformat(),
            'duration': duration
        }
        
        # 添加到临时黑名单
        self.redis_client.setex(f'security:temp_blacklist:{ip}', duration, reason)
        
        # 记录黑名单事件
        blacklist_key = f"blacklist:{int(time.time())}:{ip}"
        self.redis_client.setex(blacklist_key, 86400 * 30, json.dumps(blacklist_data))
        
        self.stats['ips_blacklisted'] += 1

    def _flag_suspicious_ip(self, ip: str, reason: str):
        """标记可疑IP"""
        flag_data = {
            'ip': ip,
            'reason': reason,
            'timestamp': datetime.utcnow().isoformat()
        }
        
        flag_key = f"suspicious_ip:{ip}"
        self.redis_client.setex(flag_key, 86400, json.dumps(flag_data))

    def _analyze_threats(self):
        """分析威胁趋势"""
        try:
            # 获取最近的威胁数据
            threats = []
            for key in self.redis_client.scan_iter(match="threat:*"):
                threat_data = self.redis_client.get(key)
                if threat_data:
                    threats.append(json.loads(threat_data))
            
            # 分析威胁趋势
            if len(threats) > 50:  # 威胁数量过多
                self._send_high_threat_alert(len(threats))
            
        except Exception as e:
            print(f"威胁分析失败: {str(e)}")

    def _update_statistics(self):
        """更新统计信息"""
        try:
            # 更新Redis中的统计信息
            stats_key = "security:stats"
            self.redis_client.setex(stats_key, 3600, json.dumps(self.stats))
            
        except Exception as e:
            print(f"更新统计信息失败: {str(e)}")

    def _send_alert(self, threat_data: Dict):
        """发送告警"""
        if self.alert_config['email']['enabled']:
            self._send_email_alert(threat_data)
        
        if self.alert_config['webhook']['enabled']:
            self._send_webhook_alert(threat_data)
        
        if self.alert_config['sms']['enabled']:
            self._send_sms_alert(threat_data)

    def _send_email_alert(self, threat_data: Dict):
        """发送邮件告警"""
        try:
            config = self.alert_config['email']
            
            msg = MIMEMultipart()
            msg['From'] = config['username']
            msg['To'] = ', '.join(config['recipients'])
            msg['Subject'] = f"安全告警: {threat_data['threat_type']}"
            
            body = f"""
            检测到安全威胁:
            
            威胁类型: {threat_data['threat_type']}
            IP地址: {threat_data['ip']}
            触发次数: {threat_data['count']}
            处理动作: {threat_data['action']}
            时间: {threat_data['timestamp']}
            """
            
            msg.attach(MIMEText(body, 'plain'))
            
            server = smtplib.SMTP(config['smtp_server'], config['smtp_port'])
            server.starttls()
            server.login(config['username'], config['password'])
            server.send_message(msg)
            server.quit()
            
        except Exception as e:
            print(f"发送邮件告警失败: {str(e)}")

    def _send_webhook_alert(self, threat_data: Dict):
        """发送Webhook告警"""
        try:
            config = self.alert_config['webhook']
            
            payload = {
                'alert_type': 'security_threat',
                'data': threat_data
            }
            
            response = requests.post(
                config['url'],
                json=payload,
                headers=config['headers'],
                timeout=10
            )
            
            if response.status_code != 200:
                print(f"Webhook告警发送失败: {response.status_code}")
                
        except Exception as e:
            print(f"发送Webhook告警失败: {str(e)}")

    def _send_sms_alert(self, threat_data: Dict):
        """发送短信告警"""
        try:
            config = self.alert_config['sms']
            
            message = f"安全告警: {threat_data['threat_type']} - IP: {threat_data['ip']}"
            
            for recipient in config['recipients']:
                payload = {
                    'to': recipient,
                    'message': message,
                    'api_key': config['api_key']
                }
                
                response = requests.post(config['api_url'], json=payload, timeout=10)
                
        except Exception as e:
            print(f"发送短信告警失败: {str(e)}")

    def _send_high_threat_alert(self, threat_count: int):
        """发送高威胁级别告警"""
        alert_data = {
            'alert_type': 'high_threat_level',
            'threat_count': threat_count,
            'timestamp': datetime.utcnow().isoformat()
        }
        
        self._send_alert(alert_data)

    def get_security_dashboard_data(self) -> Dict:
        """获取安全仪表板数据"""
        try:
            # 获取统计信息
            stats = self.stats.copy()
            
            # 获取最近的威胁
            recent_threats = []
            for key in self.redis_client.scan_iter(match="threat:*"):
                threat_data = self.redis_client.get(key)
                if threat_data:
                    recent_threats.append(json.loads(threat_data))
            
            # 按时间排序，取最近10个
            recent_threats.sort(key=lambda x: x['timestamp'], reverse=True)
            recent_threats = recent_threats[:10]
            
            # 获取黑名单IP
            blacklisted_ips = []
            for key in self.redis_client.scan_iter(match="security:temp_blacklist:*"):
                ip = key.split(':')[-1]
                reason = self.redis_client.get(key)
                ttl = self.redis_client.ttl(key)
                blacklisted_ips.append({
                    'ip': ip,
                    'reason': reason,
                    'expires_in': ttl
                })
            
            return {
                'stats': stats,
                'recent_threats': recent_threats,
                'blacklisted_ips': blacklisted_ips,
                'monitor_status': 'running' if self.running else 'stopped'
            }
            
        except Exception as e:
            print(f"获取仪表板数据失败: {str(e)}")
            return {}

    def configure_alerts(self, alert_config: Dict):
        """配置告警设置"""
        self.alert_config.update(alert_config)
        
        # 保存配置到Redis
        config_key = "security:alert_config"
        self.redis_client.setex(config_key, 86400 * 30, json.dumps(self.alert_config))

    def add_threat_rule(self, rule_name: str, rule_config: Dict):
        """添加威胁检测规则"""
        self.threat_rules[rule_name] = rule_config
        
        # 保存规则到Redis
        rules_key = "security:threat_rules"
        self.redis_client.setex(rules_key, 86400 * 30, json.dumps(self.threat_rules))

    def remove_from_blacklist(self, ip: str):
        """从黑名单移除IP"""
        self.redis_client.delete(f'security:temp_blacklist:{ip}')
        print(f"IP {ip} 已从黑名单移除")

    def get_threat_statistics(self, hours: int = 24) -> Dict:
        """获取威胁统计"""
        try:
            cutoff_time = datetime.now() - timedelta(hours=hours)
            
            threats = []
            for key in self.redis_client.scan_iter(match="threat:*"):
                threat_data = self.redis_client.get(key)
                if threat_data:
                    threat = json.loads(threat_data)
                    threat_time = datetime.fromisoformat(threat['timestamp'].replace('Z', '+00:00'))
                    if threat_time >= cutoff_time:
                        threats.append(threat)
            
            # 统计威胁类型
            threat_types = defaultdict(int)
            threat_ips = defaultdict(int)
            
            for threat in threats:
                threat_types[threat['threat_type']] += 1
                threat_ips[threat['ip']] += 1
            
            return {
                'total_threats': len(threats),
                'threat_types': dict(threat_types),
                'top_threat_ips': dict(sorted(threat_ips.items(), key=lambda x: x[1], reverse=True)[:10]),
                'time_range': f"{hours} hours"
            }
            
        except Exception as e:
            print(f"获取威胁统计失败: {str(e)}")
            return {}